package state.component;

import java.util.Vector;

import javax.microedition.lcdui.Display;

import state.Clip;
import state.GameConstants;
import state.GameGlobal;
import state.Graphics;
import state.Key;
import state.Manager;
import state.Sprite;
import state.TimerListener;
import state.font.FontManager;
import state.font.ImageText;
import state.util.Color;

public class TextField extends Component {
	public static final long DEFAULT_KEY_DELAY = 1000;
	
	public static final int CONSTRAINT_ANY = 0;
	public static final int CONSTRAINT_PASSWORD = 1;
	public static final int CONSTRAINT_USER_NAME = 2;
	public static final int CONSTRAINT_PHONE_NUMBER = 3;// allow input phone number
	public static final int CONSTRAINT_NUMERIC = 4; // allow input integer number
  public static final int CONSTRAINT_EMAIL_ADDRESS = 5; // allow input email address
//  public static final int CONSTRAINT_URL = 6; // allow input url

	private static final char[][] REPLACE_CHAR = new char[][] {{'ư', 'w'}, {'Ư', 'W'}, {'ơ', 'o'}, {'Ơ', 'O'}, {'Đ', 'D'}, {'đ', 'd'}, {'Ă', 'A'}, {'ă', 'a'}, {'Â', 'A'}, {'â', 'a'}};

	private static final char[][][] CONSTRAINT_KEY_MAP = new char[][][]{
		{ // CONSTRAINT_ANY
			{'.', ',', '?', '!', '\'', '\"', '1', '-', '(', ')', '@', '/', ':', '_'}, 
			{'a', 'b', 'c', '2'}, 
			{'d', 'e', 'f', '3'}, 
			{'g', 'h', 'i', '4'}, 
			{'j', 'k', 'l', '5'}, 
			{'m', 'n', 'o', '6'}, 
			{'p', 'q', 'r', 's', '7'}, 
			{'t', 'u', 'v', '8'}, 
			{'w', 'x', 'y', 'z', '9'}, 
			{' ', '0'},
			{'*'}
		}, 
		{ // CONSTRAINT_PASSWORD
			{'1'}, 
			{'a', 'b', 'c', '2'}, 
			{'d', 'e', 'f', '3'}, 
			{'g', 'h', 'i', '4'}, 
			{'j', 'k', 'l', '5'}, 
			{'m', 'n', 'o', '6'}, 
			{'p', 'q', 'r', 's', '7'}, 
			{'t', 'u', 'v', '8'}, 
			{'w', 'x', 'y', 'z', '9'}, 
			{'0'}, 
			{}
		}, 
		{ // CONSTRAINT_USER_NAME
			{'1'}, 
			{'a', 'b', 'c', '2'}, 
			{'d', 'e', 'f', '3'}, 
			{'g', 'h', 'i', '4'}, 
			{'j', 'k', 'l', '5'}, 
			{'m', 'n', 'o', '6'}, 
			{'p', 'q', 'r', 's', '7'}, 
			{'t', 'u', 'v', '8'}, 
			{'w', 'x', 'y', 'z', '9'}, 
			{'0'}, 
			{}
		}, 
		{ // CONSTRAINT_PHONE_NUMBER
			{'1'}, 
			{'2'}, 
			{'3'}, 
			{'4'}, 
			{'5'}, 
			{'6'}, 
			{'7'}, 
			{'8'}, 
			{'9'}, 
			{'0'}, 
			{}
		}, 
		{ // CONSTRAINT_NUMERIC
			{'1'}, 
			{'2'}, 
			{'3'}, 
			{'4'}, 
			{'5'}, 
			{'6'}, 
			{'7'}, 
			{'8'}, 
			{'9'}, 
			{'0'}, 
			{}
		}, 
		{ // CONSTRAINT_EMAIL_ADDRESS
		  {'.', '@', '_', '1'}, 
      {'a', 'b', 'c', '2'}, 
      {'d', 'e', 'f', '3'}, 
      {'g', 'h', 'i', '4'}, 
      {'j', 'k', 'l', '5'}, 
      {'m', 'n', 'o', '6'}, 
      {'p', 'q', 'r', 's', '7'}, 
      {'t', 'u', 'v', '8'}, 
      {'w', 'x', 'y', 'z', '9'}, 
      {' ', '0'},
      {'*'}
		}
	};

	private StringBuffer showText;
	private StringBuffer realText;
	
	private int textX;
	private int textY;

	private int constraint = CONSTRAINT_ANY;
	private int align = TextFieldStyle.ALIGN_LEFT;
	private int maxLength = 100;
	
	private int lastKeyCode;
	private long lastTimeKey;
	private int lastCharIndex;
	private boolean isShowLineOnFocus;
	private long lastTimeDrawLine;
	private long lastTimePressChar;
	private boolean isUpperChar;
	private Clip reusedClip = new Clip(0, 0, 0, 0);
	
	private int clearCharKey = Key.BACKSPACE;
	
	private static TimerListener timerListener;
	private static Vector allTextFields = new Vector();
	
	private SystemTextField systemTextField;
	
	private static void initTimer() {
	  if (timerListener == null) {
	    timerListener = new TimerListener() {
        public void doTask() {
          for (int i = 0; i < allTextFields.size(); i++) {
            TextField textField = (TextField) allTextFields.elementAt(i);
            if (textField.constraint == CONSTRAINT_PASSWORD) {
              if ((textField.showText.length() > 0) && (textField.lastTimePressChar > -1) && (System.currentTimeMillis() - textField.lastTimePressChar > DEFAULT_KEY_DELAY)) {
                textField.showText.setCharAt(textField.showText.length() - 1, '*');
                textField.lastTimePressChar = -1;
              }
            }
          }
        }
      };
      GameGlobal.systemCanvas.getTimer().addTarget(timerListener);
	  }
	}
	
	public TextField(Manager manager, int constraint) {
	  this.constraint = constraint;
    setManager(manager);
  }
  
  public TextField(Manager manager, int constraint, TextFieldStyle style) {
    this.constraint = constraint;
    setStyle(style);
    setManager(manager);
  }
  
	protected void init() {
		super.init();
		initTimer();
		allTextFields.addElement(this);
		showText = new StringBuffer();
		realText = new StringBuffer();
		lastTimePressChar = -1;
	}
	
	public TextField setStyle(TextFieldStyle style) {
		super.setStyle(style);
		setAlign(style.align);
		return this;
	}
	
	public Component setStyle(Style style) {
	  super.setStyle(style);
	  if (style instanceof TextFieldStyle) {
	    setAlign(((TextFieldStyle) style).align);
	  }
	  return this;
  }

  /**
	 * User for screen 240x320
	 */
	private char[] getChars(int keyCode) {
		switch (keyCode) {
		case Key.K_1:
		case Key.K_2:
		case Key.K_3:
		case Key.K_4:
		case Key.K_5:
		case Key.K_6:
		case Key.K_7:
		case Key.K_8:
		case Key.K_9:
			return CONSTRAINT_KEY_MAP[constraint][keyCode - Key.K_1];
		case Key.K_0:
			return CONSTRAINT_KEY_MAP[constraint][9];
		case Key.STAR:
			return CONSTRAINT_KEY_MAP[constraint][10];
		}
		return null;
	}
	
	private char getChar(int keyCode) {
		char c = (char) keyCode;
		return checkReplaceChar(c);
	}
	
	private char checkReplaceChar(char c) {
		for (int i = 0; i < REPLACE_CHAR.length; i++) {
			if (c == REPLACE_CHAR[i][0]) {
				return REPLACE_CHAR[i][1];
			}
		}
		return c;
	}
	
	private boolean isCharValid(char c) {
	  char[][] chars = CONSTRAINT_KEY_MAP[constraint];
	  for (int i = 0; i < chars.length; i++) {
      for (int j = 0; j < chars[i].length; j++) {
        if (c == chars[i][j]) {
          return true;
        }
      }
    }
	  return false;
	}
	
	private void drawCharSuggest(Graphics g) {
	  if (!GameConstants.IS_240x320_SCREEN) {
	    return;
	  }
	  ImageText font = getFont();
	  long now = System.currentTimeMillis();
    if ((now - lastTimeKey < DEFAULT_KEY_DELAY) && (lastKeyCode != 0)) {
      char[] keyMap = getChars(lastKeyCode);
      int allCharWidth = font.stringWidth(String.valueOf(keyMap)) + keyMap.length * font.charWidth(' ');
      Clip.getClip(g, reusedClip);
      g.setClip(0, 0, GameConstants.SCREEN_WIDTH, GameConstants.SCREEN_HEIGHT);
      int startX = realX + width - allCharWidth;
      int startY = realY - font.getHeight() + 2;
      g.setColor(0x11A8ED);
      g.fillRect(startX - 2, startY, allCharWidth + 4, font.getHeight() + 2);
      for (int i = 0; i < keyMap.length; i++) {
        if (keyMap[i] == keyMap[lastCharIndex]) {
          if (FontManager.MIDP_VERSION < 2.0) {
            g.setColor(Color.WHITE_CODE);
          } else {
            g.setColor(Color.BLACK_CODE);
          }
          g.fillRect(startX, startY, font.charWidth(keyMap[i]), font.getHeight() + 2);
        }
        if (isUpperChar) {
          font.drawOneChar(g, String.valueOf(keyMap[i]).toUpperCase().charAt(0), Color.WHITE_CODE, startX, startY);
        } else {
          font.drawOneChar(g, keyMap[i], Color.WHITE_CODE, startX, startY);
        }
        startX += font.charWidth(keyMap[i]) + font.charWidth(' ');
      }
      Clip.setClip(g, reusedClip);
    }
	}

	public void draw(Graphics g) {
		if (!isVisible) {
			return;
		}
		
		if (isFillBackGround) {
		  if (focusable && isFocused) {
		    drawCharSuggest(g);
		    g.setColor(focusBgColor);
		  } else {
		    g.setColor(backgroundColor);
		  }
			g.fillRoundRect(realX, realY, width, height, 5, 5);
		}

		if (hasBorder) {
			g.setColor(borderColor);
			g.drawRoundRect(realX, realY, width, height, 5, 5);
		}

		ImageText font = getFont();
		if (focusable && isFocused) {
		  font.drawString(g, showText.toString(), focusFgColor, textX, textY, align);
		  if (isShowLineOnFocus) {
		    int lineX = textX + font.stringWidth(showText.toString()) + 1;
		    g.setColor(focusFgColor);
	      g.drawLine(lineX, textY - font.getHeight() / 2 + 1, lineX, textY + font.getHeight() / 2 - 1);
		  }
		  
		  long now = System.currentTimeMillis();
		  if (now - lastTimeDrawLine > DEFAULT_KEY_DELAY) {
		    isShowLineOnFocus = !isShowLineOnFocus;
		    lastTimeDrawLine = now;
		    if (constraint == CONSTRAINT_PASSWORD) {
		      if (this.showText.length() > 0) {
		        this.showText.deleteCharAt(this.showText.length() - 1);
	          this.showText.append('*');
		      }
		    }
		  }
    } else {
      font.drawString(g, showText.toString(), forceGroundColor, textX, textY, align);
    }
	}

	public void keyReleased(int keyCode) {
		if (GameConstants.IS_240x320_SCREEN) {
		  keyReleased240x320(keyCode);
		} else {
		  keyReleased320x240(keyCode);
		}
	}
	
	private void keyReleased240x320(int keyCode) {
	  if (keyCode == clearCharKey) {
      deleteLastChar();
      return;
    }
	  
	  if (keyCode == Key.POUND || keyCode == Key.SHIFT) {
	    isUpperChar = !isUpperChar;
	    return;
	  }
	  
	  char[] keyMap = getChars(keyCode);
	  if ((keyMap == null) || (keyMap.length == 0)) {
	    return;
	  }
	  
	  long now = System.currentTimeMillis();
    if (keyCode == lastKeyCode) {
      if (now - lastTimeKey < DEFAULT_KEY_DELAY) {
        lastCharIndex = (lastCharIndex + 1) % keyMap.length;
        if (isUpperChar) {
          replaceLastChar(String.valueOf(keyMap[lastCharIndex]).toUpperCase().charAt(0));
        } else {
          replaceLastChar(keyMap[lastCharIndex]);
        }
      } else {
        lastCharIndex = 0;
        if (isUpperChar) {
          append(String.valueOf(keyMap[lastCharIndex]).toUpperCase().charAt(0));
        } else {
          append(keyMap[lastCharIndex]);
        }
      }
    } else {
      if (constraint == CONSTRAINT_PASSWORD) {
        if (this.showText.length() > 0) {
          this.showText.deleteCharAt(this.showText.length() - 1);
          this.showText.append('*');
        }
      }
      
      lastCharIndex = 0;
      lastKeyCode = keyCode;
      if (isUpperChar) {
        append(String.valueOf(keyMap[lastCharIndex]).toUpperCase().charAt(0));
      } else {
        append(keyMap[lastCharIndex]);
      }
    }
    lastTimeKey = now;
    lastTimeDrawLine = now;
    isShowLineOnFocus = false;
  }
	
	private void keyReleased320x240(int keyCode) {
	  if (keyCode == clearCharKey) {
      deleteLastChar();
      return;
    }
	  
	  if (keyCode == Key.POUND || keyCode == Key.SHIFT) {
      isUpperChar = !isUpperChar;
      return;
    }
	  
    if (!Key.isCommandKey) {
      char c = getChar(keyCode);
      if (!isCharValid(c)) {
        return;
      }
      
      if (isUpperChar) {
        append(String.valueOf(c).toUpperCase().charAt(0));
      } else {
        append(c);
      }
    }
  }

	private int getTextY() {
		return realY + height / 2;
	}

	private int getTextX() {
		int textX = realX + 2;
		switch (align) {
		case TextFieldStyle.ALIGN_LEFT:
			textX = realX + 2;
			break;
		case TextFieldStyle.ALIGN_CENTER:
			textX = realX + width / 2;
			anchor = GameConstants.CENTER_ANCHOR;
			break;
		case TextFieldStyle.ALIGN_RIGHT:
			textX = realX + width - 2;
			anchor = GameConstants.VCENTER_RIGHT_ANCHOR;
			break;
		}
		return textX;
	}
	
	public Component setFont(ImageText font) {
	  super.setFont(font);
	  updateTextPosition();
	  return this;
	}

	public int getConstraint() {
		return constraint;
	}

	public TextField setConstraint(int constraint) {
		this.constraint = constraint;
		return this;
	}

	public int getAlign() {
		return align;
	}

	public TextField setAlign(int align) {
		this.align = align;
		updateTextPosition();
		return this;
	}
	
	public Sprite setHeight(int height) {
    super.setHeight(height);
    updateTextPosition();
    return this;
  }

  public Sprite setPosition(int x, int y) {
    super.setPosition(x, y);
    updateTextPosition();
    return this;
  }
  
  public Sprite move(int dx, int dy) {
    super.move(dx, dy);
    updateTextPosition();
    return this;
  }

  public Sprite setSize(int width, int height) {
    super.setSize(width, height);
    updateTextPosition();
    return this;
  }

  public Sprite setWidth(int width) {
    super.setWidth(width);
    updateTextPosition();
    return this;
  }

  private void updateTextPosition() {
	  textX = getTextX();
    textY = getTextY();
	}

	public int getMaxLength() {
		return maxLength;
	}

	public TextField setMaxLength(int maxLength) {
		this.maxLength = maxLength;
		return this;
	}

	public String getText() {
		return realText.toString();
	}
	
	private void replaceLastChar(char c) {
	  if(this.realText.length() == 0) {
	    return;
	  }
	  this.realText.deleteCharAt(this.realText.length() - 1);
	  this.realText.append(c);
	  
    this.showText.deleteCharAt(this.showText.length() - 1);
    this.showText.append(c);
	}
	
	public TextField setText(String text) {
	  this.realText.setLength(0);
    this.realText.append(text);
	  
		this.showText.setLength(0);
		if (constraint == CONSTRAINT_PASSWORD) {
		  for (int i = 0; i < text.length(); i++) {
		    this.showText.append('*');
      }
		  lastTimePressChar = System.currentTimeMillis();
		} else {
		  this.showText.append(text);
		}
		return this;
	}
	
	public TextField append(String text) {
	  if (text.length() > maxLength) {
	    text = text.substring(0, maxLength);
	  }
	  
	  this.realText.append(text);
	  if (constraint == CONSTRAINT_PASSWORD) {
      for (int i = 0; i < text.length(); i++) {
        this.showText.append('*');
      }
      checkToReplaceToStar();
      lastTimePressChar = System.currentTimeMillis();
    } else {
      this.showText.append(text);
    }
	  return this;
	}
	
	public TextField append(char text) {
	  if (realText.length() >= maxLength) {
	    return this;
	  }
	  
    this.realText.append(text);
    this.showText.append(text);
    if (constraint == CONSTRAINT_PASSWORD) {
      checkToReplaceToStar();
    }
    lastTimePressChar = System.currentTimeMillis();
    return this;
  }
	
	public TextField clearText() {
	  this.realText.setLength(0);
	  this.showText.setLength(0);
	  return this;
	}
	
	public TextField deleteLastChar() {
	  if (this.realText.length() > 0) {
	    this.realText.deleteCharAt(this.realText.length() - 1);
	    this.showText.deleteCharAt(this.showText.length() - 1);
	  }
	  return this;
	}
	
	public int getClearCharKey() {
    return clearCharKey;
  }

  public TextField setClearCharKey(int clearCharKey) {
    this.clearCharKey = clearCharKey;
    return this;
  }
  
  public void pointerReleased(Sprite source, int x, int y) {
    if (systemTextField == null) {
      systemTextField = new SystemTextField(this);
    }
    systemTextField.refesh();
    Display.getDisplay(GameGlobal.systemCanvas.getMidlet()).setCurrent(systemTextField);
    manager.setFocus(this);
  }

  public Sprite detroy() {
		super.detroy();
		allTextFields.removeElement(this);
		return this;
	}
  
  private void checkToReplaceToStar() {
    for (int i = 0; i < showText.length() - 1; i++) {
      this.showText.setCharAt(i, '*');
    } 
  }
}
